/**
 * 摄像机跟随配置接口
 * @property entity 玩家实体
 * @property cameraEntity 摄像机实体
 * @property cameraDelay 摄像机呼吸速度，数值越小，速度越快
 * @property breatheAmplitudeY 摄像机Y轴摆动幅度，数值越大摆动越大
 * @property radiusMultiplierY 摄像机Y轴摆动半径倍数，在摆动基础上×对应的倍数
 * @property breatheAmplitudeX 摄像机X轴摆动幅度，数值越大摆动越大
 * @property radiusMultiplierX 摄像机X轴摆动半径倍数，在摆动基础上×对应的倍数
 * @property breatheAmplitudeZ 摄像机Z轴摆动幅度，数值越大摆动越大
 * @property radiusMultiplierZ 摄像机Z轴摆动半径倍数，在摆动基础上×对应的倍数
 */
interface CameraLagConfig {
    /** 玩家实体 */
    entity: GameEntity;
    /** 摄像机实体 */
    cameraEntity: GameEntity;
    /** 摄像机延迟时间 */
    cameraDelay?: number;
    /** 摄像机Y轴摆动幅度 */
    breatheAmplitudeY?: number;
    /** 摄像机Y轴摆动半径倍数 */
    radiusMultiplierY?: number;
    /** 摄机X轴摆动幅度 */
    breatheAmplitudeX?: number;
    /** 摄像机X轴摆动半径倍数 */
    radiusMultiplierX?: number;
    /** 摄像机Z轴摆动幅度 */
    breatheAmplitudeZ?: number;
    /** 摄像机Z轴摆动半径倍数 */
    radiusMultiplierZ?: number;
}

export class CameraLag {
    private degre: number;
    private time: number | null;
    private epos: GameVector3;

    public e: GameEntity;
    public e1: GameEntity;
    public delay: number;
    public breatheAmplitudeY: number;
    public radiusMultiplierY: number;
    public breatheAmplitudeX: number;
    public radiusMultiplierX: number;
    public breatheAmplitudeZ: number;
    public radiusMultiplierZ: number;

    constructor(config: CameraLagConfig) {
        this.e = config.entity;
        this.e1 = config.cameraEntity;
        this.delay = config.cameraDelay ?? 0.01;
        this.breatheAmplitudeY = config.breatheAmplitudeY ?? 20;
        this.radiusMultiplierY = config.radiusMultiplierY ?? 0.3;
        this.breatheAmplitudeX = config.breatheAmplitudeX ?? 60;
        this.radiusMultiplierX = config.radiusMultiplierX ?? 0.2;
        this.breatheAmplitudeZ = config.breatheAmplitudeZ ?? 60;
        this.radiusMultiplierZ = config.radiusMultiplierZ ?? 0.2;
        this.degre = 0;
        this.time = null;
        this.epos = this.e.position;
        this.sete1Position();
        this.e1.meshInvisible = true;
    }

    /** 实体与摄像机位置同步 */
    public sete1Position(): void {
        this.e1.position = new GameVector3(
            this.epos.x,
            this.epos.y,
            this.epos.z
        );
    }

    /** 跟随运行 */
    public async play(): Promise<void> {
        this.time = (await setInterval(() => {
            this.degre += this.delay;
            this.sete1XYZPosition();
        }, 16)) as unknown as number;
    }

    /**
     * 结束跟随
     * @param b 是否销毁摄像机模型
     */
    public pause(b: boolean = false): void {
        if (this.time) {
            clearInterval(this.time);
        }
        if (b) {
            this.e1.destroy();
        }
    }

    /** 游戏视角回到摄像机 */
    public toCameraEntity(): void {
        if (this.e.player) {
            this.e.player.cameraEntity = this.e1;
        }
    }

    /** 游戏视角回到玩家 */
    public toPlayerEntity(): void {
        if (this.e.player) {
            this.e.player.cameraEntity = this.e;
        }
    }

    /**
     * 设置摄像机视距
     * @param vector3 模型缩放大小
     */
    public setCameraEntityMeshScale(vector3: GameVector3): void {
        this.e1.meshScale = vector3;
    }

    /**
     * Sigmoid函数求导，用于非线性平滑
     * @param x 输入值
     * @returns 导数值
     */
    private derivative(x: number): number {
        const sx = this.sigmoid(x);
        return sx * (1 - sx);
    }

    /**
     * Sigmoid函数计算
     * @param x 输入值
     * @returns 计算结果
     */
    private sigmoid(x: number): number {
        const ex = Math.E ** x;
        return ex / (ex + 1);
    }

    /** 设置摄像机摆动 */
    private sete1XYZPosition(): void {
        this.e1.position = new GameVector3(
            this.epos.x +
                this.cos(this.breatheAmplitudeX, this.radiusMultiplierX),
            this.epos.y +
                this.radiusMultiplierY *
                    this.derivative(
                        this.breatheAmplitudeY *
                            this.sin(
                                this.breatheAmplitudeY,
                                this.radiusMultiplierY
                            )
                    ),
            this.epos.z +
                this.cos(this.breatheAmplitudeZ, this.radiusMultiplierZ)
        );
    }

    /**
     * 计算X/Z轴的正弦波值
     * @param breatheAmplitude 摆动幅度
     * @param radiusMultiplier 半径倍数
     * @returns 计算结果
     */
    private sin(breatheAmplitude: number, radiusMultiplier: number): number {
        return (
            Math.sin(((breatheAmplitude * Math.PI) / 360) * this.degre) *
            radiusMultiplier
        );
    }

    /**
     * 计算X/Z轴的余弦波值
     * @param breatheAmplitude 摆动幅度
     * @param radiusMultiplier 半径倍数
     * @returns 计算结果
     */
    private cos(breatheAmplitude: number, radiusMultiplier: number): number {
        return (
            Math.cos(((breatheAmplitude * Math.PI) / 360) * this.degre) *
            radiusMultiplier
        );
    }
}
